import ast
import collections
import contextlib
import datetime
import functools
import hashlib
import hmac
import inspect
import logging
import mimetypes
import os
import pprint
import random
import re
import sys
import threading
import time
import traceback
import warnings
from os.path import join as opj
from zlib import adler32

import babel.core
from datetime import datetime, date
import passlib.utils
import psycopg2
import json
import werkzeug.contrib.sessions
import werkzeug.datastructures
import werkzeug.exceptions
import werkzeug.local
import werkzeug.routing
import werkzeug.wrappers
import werkzeug.wsgi
from werkzeug import urls
from werkzeug.wsgi import wrap_file

try:
    import psutil
except ImportError:
    psutil = None

import odoo
from odoo import fields
from .service.server import memory_info
from .service import security, model as service_model
from .tools.func import lazy_property
from .tools import ustr, consteq, frozendict, pycompat, unique, date_utils

from .modules.module import module_manifest

_logger = logging.getLogger(__name__)
rpc_request = logging.getLogger(__name__ + '.rpc.request')
rpc_response = logging.getLogger(__name__ + '.rpc.response')

STATIC_CACHE = 60 * 60 * 24 * 7

# To remove when corrected in Babel
babel.core.LOCALE_ALIASES['nb'] = 'nb_NO'

_request_stack = werkzeug.local.LocalStack()

request = _request_stack()

def replace_request_password(args):
    pass

NO_POSTMORTEM = (odoo.osv.orm.except_orm,
                 odoo.exceptions.AccessError,
                 odoo.exceptions.ValidationError,
                 odoo.exceptions.MissingError,
                 odoo.exceptions.AccessDenied,
                 odoo.exceptions.Warning,
                 odoo.exceptions.RedirectWarning)
def dispatch_rpc(service_name, method, params):
    pass

def local_redirect(path, query=None, keep_hash=False, forward_debug=True, code=303):
    pass

def redirect_with_hash(url, code=303):
    pass

class WebRequest(object):
    def __init__(self, httprequest):
        self.httprequest = httprequest
        self.httpresponse = None
        self.disable_db = False
        self.endpoint = None
        self.endpoint_arguments = None
        self.auth_method = None
        self._cr = None
        self._uid = None
        self._context = None
        self._env = None
        self._failed = None
        if self.db:
            threading.current_thread().dbname = self.db
        if self.session.uid:
            threading.current_thread().uid = self.session.uid


    @property
    def cr(self):
        pass

    @property
    def uid(self):
        pass

    @uid.setter
    def uid(self, val):
        pass

    @property
    def context(self):
        pass

    @context.setter
    def context(self, val):
        pass

    @property
    def env(self):
        pass

    @lazy_property
    def lang(self):
        pass

    @lazy_property
    def session(self):
        return self.httprequest.session

    def __enter__(self):
        _request_stack.push(self)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass

    def set_handler(self, endpoint, arguments, auth):
        arguments = {k: v for k, v in arguments.items()
                         if not k.startswith("_ignored_")}
        self.endpoint_arguments = arguments
        self.endpoint = endpoint
        self.auth_method = auth

    def _handle_exception(self, exception):
        pass

    def _call_function(self, *args, **kwargs):
        request = self
        if self.endpoint.routing['type'] != self._request_type:
            pass

        if self.endpoint_arguments:
            pass

        if self.endpoint.first_arg_is_req:
            pass

        @service_model.check
        def checked_call(___dbname, *a, **kw):
            pass

        if self.db:
            pass
        return self.endpoint(*args, **kwargs)

    @property
    def debug(self):
        pass

    @contextlib.contextmanager
    def registry_cr(self):
        pass

    @property
    def registry(self):
        pass

    @property
    def db(self):
        return self.session.db if not self.disable_db else None

    def csrf_token(self, time_limit=3600):
        pass

    def validate_csrf(self, csrf):
        pass

def route(route=None, **kw):
    routing = kw.copy()
    assert 'type' not in routing or routing['type'] in ("http", "json")
    def decorator(f):
        if route:
            if isinstance(route, list):
                routes = route
            else:
                routes = [route]
            routing['routes'] = routes

        @functools.wraps(f)
        def response_wrap(*args, **kw):
            response = f(*args, **kw)
        response_wrap.routing = routing
        response_wrap.original_func = f
        return response_wrap

    return decorator

class JsonRequest(WebRequest):
    _request_type = "json"

    def __init__(self, *args):
        pass

    def _json_response(self, result=None, error=None):
        pass

    def _handle_exception(self, exception):
        pass

    def dispatch(self):
        pass

def serialize_exception(e):
    pass

class HttpRequest(WebRequest):
    _request_type = "http"

    def __init__(self, *args):
        super(HttpRequest, self).__init__(*args)
        params = collections.OrderedDict(self.httprequest.args)
        params.update(self.httprequest.form)
        params.update(self.httprequest.files)
        params.pop('session_id', None)
        self.params = params

    def _handle_exception(self, exception):
        pass

    def dispatch(self):
        pass
        if request.httprequest.method == 'OPTIONS' and request.endpoint and request.endpoint.routing.get('cors'):
            headers = {
                'Access-Control-Max-Age': 60 * 60 * 24,
                'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, X-Debug-Mode'
            }
            return Response(status=200, headers=headers)

        if request.httprequest.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE') \
                and request.endpoint.routing.get('csrf', True): # csrf checked by default
            token = self.params.pop('csrf_token', None)

        r = self._call_function(**self.params)


    def make_response(self, data, headers=None, cookies=None):
        pass

    def render(self, template, qcontext=None, lazy=True, **kw):
        pass

    def not_found(self, description=None):
        pass

addons_manifest = {}
controllers_per_module = collections.defaultdict(list)

class ControllerType(type):
    def __init__(cls, name, bases, attrs):
        super(ControllerType, cls).__init__(name, bases, attrs)
        for k, v in attrs.items():
            if inspect.isfunction(v) and hasattr(v, 'original_func'):

                routing_type = v.routing.get('type')
                parent = [claz for claz in bases if isinstance(claz, ControllerType) and hasattr(claz, k)]
                parent_routing_type = getattr(parent[0], k).original_func.routing_type if parent else routing_type or 'http'
                if routing_type is not None and routing_type is not parent_routing_type:
                    pass
                v.original_func.routing_type = routing_type or parent_routing_type

                spec = inspect.getargspec(v.original_func)
                first_arg = spec.args[1] if len(spec.args) >= 2 else None
                if first_arg in ["req", "request"]:
                    pass


        name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls)
        class_path = name_class[0].split(".")
        if not class_path[:2] == ["odoo", "addons"]:
            module = ""
        else:
            module = class_path[2]

        if not "Controller" in globals() or not Controller in bases:
            return
        controllers_per_module[module].append(name_class)

Controller = ControllerType('Controller', (object,), {})

class EndPoint(object):
    def __init__(self, method, routing):
        pass

    @property
    def first_arg_is_req(self):
        return getattr(self.method, '_first_arg_is_req', False)

    def __call__(self, *args, **kw):
        pass

def routing_map(modules, nodb_only, converters=None):
    pass

class AuthenticationError(Exception):
    pass

class SessionExpiredException(Exception):
    pass

class OpenERPSession(werkzeug.contrib.sessions.Session):
    def __init__(self, *args, **kwargs):
        self.inited = False
        self.modified = False
        self.rotate = False
        super(OpenERPSession, self).__init__(*args, **kwargs)
        self.inited = True
        self._default_values()
        self.modified = False

    def __getattr__(self, attr):
        return self.get(attr, None)

    def __setattr__(self, k, v):
        if getattr(self, "inited", False):
            try:
                object.__getattribute__(self, k)
            except:
                return self.__setitem__(k, v)

        object.__setattr__(self, k, v)

    def authenticate(self, db, login=None, password=None, uid=None):
        pass

    def check_security(self):
        pass

    def logout(self, keep_db=False):
        pass

    def _default_values(self):
        self.setdefault("db", None)
        self.setdefault("uid", None)
        self.setdefault("login", None)
        self.setdefault("session_token", None)
        self.setdefault("context", {})

    def get_context(self):
        pass

    def _fix_lang(self, context):
        pass

    def save_action(self, action):
        pass

    def get_action(self, key):
        pass

    def save_request_data(self):
        pass

    @contextlib.contextmanager
    def load_request_data(self):
        pass

def session_gc(session_store):
    if random.random() < 0.001:
        pass

mimetypes.add_type('application/font-woff', '.woff')
mimetypes.add_type('application/vnd.ms-fontobject', '.eot')
mimetypes.add_type('application/x-font-ttf', '.ttf')
mimetypes.add_type('image/svg+xml', '.svg')

class Response(werkzeug.wrappers.Response):
    default_mimetype = 'text/html'
    def __init__(self, *args, **kw):
        pass

    def set_default(self, template=None, qcontext=None, uid=None):
        pass

    @property
    def is_qweb(self):
        pass

    def render(self):
        pass

    def flatten(self):
        pass

class DisableCacheMiddleware(object):
    def __init__(self, app):
        pass

    def __call__(self, environ, start_response):
        def start_wrapped(status, headers):
            pass

        return self.app(environ, start_wrapped)

class Root(object):
    def __init__(self):
        self._loaded = False

    @lazy_property
    def session_store(self):
        pass

    @lazy_property
    def nodb_routing_map(self):
        pass

    def __call__(self, environ, start_response):
        if not self._loaded:
            pass
        return self.dispatch(environ, start_response)

    def load_addons(self):
        pass

    def setup_session(self, httprequest):
        session_gc(self.session_store)

        sid = httprequest.args.get('session_id')
        explicit_session = True
        if not sid:
            sid =  httprequest.headers.get("X-Openerp-Session-Id")
        if not sid:
            sid = httprequest.cookies.get('session_id')
            explicit_session = False
        if sid is None:
            httprequest.session = self.session_store.new()
        else:
            httprequest.session = self.session_store.get(sid)
        return explicit_session

    def setup_db(self, httprequest):
        db = httprequest.session.db
        if db:
            pass

        if not db:
            httprequest.session.db = db_monodb(httprequest)

    def setup_lang(self, httprequest):
        if "lang" not in httprequest.session.context:
            pass

    def get_request(self, httprequest):
        if httprequest.args.get('jsonp'):
            pass
        if httprequest.mimetype in ("application/json", "application/json-rpc"):
            pass
        else:
            return HttpRequest(httprequest)

    def get_response(self, httprequest, result, explicit_session):
        pass

    def dispatch(self, environ, start_response):
        try:
            httprequest = werkzeug.wrappers.Request(environ)
            httprequest.app = self
            httprequest.parameter_storage_class = werkzeug.datastructures.ImmutableOrderedMultiDict
            threading.current_thread().url = httprequest.url
            threading.current_thread().query_count = 0
            threading.current_thread().query_time = 0
            threading.current_thread().perf_t0 = time.time()

            explicit_session = self.setup_session(httprequest)
            self.setup_db(httprequest)
            self.setup_lang(httprequest)

            request = self.get_request(httprequest)

            def _dispatch_nodb():
                try:
                    func, arguments = self.nodb_routing_map.bind_to_environ(request.httprequest.environ).match()
                except werkzeug.exceptions.HTTPException as e:
                    return request._handle_exception(e)
                request.set_handler(func, arguments, "none")
                result = request.dispatch()

            with request:
                db = request.session.db
                if db:
                    pass
                else:
                    result = _dispatch_nodb()
                response = self.get_response(httprequest, result, explicit_session)
            return response(environ, start_response)

        except werkzeug.exceptions.HTTPException as e:
            return e(environ, start_response)


    def get_db_router(self, db):
        pass

def db_list(force=False, httprequest=None):
    dbs = odoo.service.db.list_dbs(force)
    return db_filter(dbs, httprequest=httprequest)

def db_filter(dbs, httprequest=None):
    httprequest = httprequest or request.httprequest
    h = httprequest.environ.get('HTTP_HOST', '').split(':')[0]
    d, _, r = h.partition('.')
    if d == "www" and r:
        d = r.partition('.')[0]
    if odoo.tools.config['dbfilter']:
        pass
    elif odoo.tools.config['db_name']:
        pass

    return dbs

def db_monodb(httprequest=None):
    httprequest = httprequest or request.httprequest

    dbs = db_list(True, httprequest)

    db_session = httprequest.session.db
    if db_session in dbs:
        pass

    if len(dbs) == 1:
        pass
    return None

def send_file(filepath_or_fp, mimetype=None, as_attachment=False, filename=None, mtime=None,
            add_etags=True, cache_timeout=STATIC_CACHE, conditional=True):
    pass

def content_disposition(filename):
    pass

class CommonController(Controller):
    @route('/gen_session_id', type='json', auth="none")
    def gen_session_id(self):
        pass

root = Root()
